/*
	main.cpp

	Title: Stairs! v1.0
	Author: Chris Haag
	Date: June 3, 2000
	URL: http://www.coders.net/drgnslyr/personal/games/stairs.html
	Description:

		Physics simulation of rigid body collisions where
		dice fall down a flight of stairs.

	Credits:

	Program written from and modeled off articles and code by
	Chris Hecker @ http://www.d6.com/users/checker/dynamics.htm. If it
	wasn't for Chris, this demo would not have come to exist so soon
	after I decided to learn the material.

	Orthonormalization function: http://members.nbci.com/_XMCM/gdemont/e3d_html/engine_3d-math__adb.htm

	GLUT Tutorial: http://www.fatech.com/tech/opengl/glut/

	GLUT Run-Time libraries, DLLs, and header files:
	http://www.xmission.com/~nate/glut.html

	Win32 TGA Loading source: Jeff Molofee @
	http://nehe.gamedev.net/tutorials/lesson25.asp

	Shadow rendering tutorial: http://reality.sgi.com/opengl/tips/

	TGA Textures: 3D Studio Max

    *** For questions regarding this specific simulation,
	e-mail Chris Haag at dragonslayer_developer@hotmail.com

	*** For questions regarding rigid body collision
	simulations in general, please visit these resources
	before asking me anything; some of the math is still
	sinking in:

	http://www.d6.com/users/checker/dynamics.htm
	http://www.cs.unc.edu/~ehmann/RigidTutorial/
	http://www.cs.cmu.edu/afs/cs/user/baraff/www/pbm/rigid2.pdf

    *** Things this simulation does not correctly handle:

    Friction
	Purely simultaneous collisions
	Air resistance
	Interpolation toward surface tolerance levels
	Speed optimizations
	Collisions on the left and right sides of the stairs
	Detections done for object A on object B, but not
		for object B on object A.
*/

#include <stdio.h>
#include "main.h"
#include "tgaload.h"
#include "timer.h"
#include <math.h>

#define FULL_SCREEN

// Array of texture ID's
GLuint g_texture_id[texCount];

void loadTextures()
{
	printf("Loading textures...");
	glEnable ( GL_TEXTURE_2D );
   glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );
   glGenTextures (1, g_texture_id);

   for (int i=0; i < texCount; i++)
   {
		g_texture_id[i] = i;
   }

   image_t   temp_image;
   glBindTexture ( GL_TEXTURE_2D, g_texture_id[texStair] );
   tgaLoad  ( "WALNUT3.tga", &temp_image, TGA_FREE | TGA_LOW_QUALITY );
   glBindTexture ( GL_TEXTURE_2D, g_texture_id[texFloor] );
   tgaLoad  ( "Oakqrtrt.tga", &temp_image, TGA_FREE | TGA_LOW_QUALITY );
   glBindTexture ( GL_TEXTURE_2D, g_texture_id[texOne] );
   tgaLoad  ( "one.tga", &temp_image, TGA_FREE | TGA_LOW_QUALITY );
   glBindTexture ( GL_TEXTURE_2D, g_texture_id[texTwo] );
   tgaLoad  ( "two.tga", &temp_image, TGA_FREE | TGA_LOW_QUALITY );
   glBindTexture ( GL_TEXTURE_2D, g_texture_id[texThree] );
   tgaLoad  ( "three.tga", &temp_image, TGA_FREE | TGA_LOW_QUALITY );
   glBindTexture ( GL_TEXTURE_2D, g_texture_id[texFour] );
   tgaLoad  ( "four.tga", &temp_image, TGA_FREE | TGA_LOW_QUALITY );
   glBindTexture ( GL_TEXTURE_2D, g_texture_id[texFive] );
   tgaLoad  ( "five.tga", &temp_image, TGA_FREE | TGA_LOW_QUALITY );
   glBindTexture ( GL_TEXTURE_2D, g_texture_id[texSix] );
   tgaLoad  ( "six.tga", &temp_image, TGA_FREE | TGA_LOW_QUALITY );
}

void Init3D(void)
{
	GLfloat lightPos[4] = { -1, 1, 0, 0 };
	GLfloat lightAmb[4] = { 0, 0, 0, 1 };
	GLfloat lightDiff[4] = { 1, 1, 1, 1 };

	// Set lighting
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);	
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
	glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiff);

	// Other stuff
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_FLAT);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}

void changeSize(int w, int h) {
	float ratio = 1.0* w / h;

	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if(h == 0)
		h = 1;

	// Reset the coordinate system before modifying
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Set the viewport to be the entire window
	glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45,ratio,10,1000);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void ProcessKeyboard(unsigned char key, int x, int y)
{
	FreeSystems();
	exit(0);
}

void positionCamera(void)
{
	glLoadIdentity();
	gluLookAt(260,300,-330, 0,100,-50, 0,1,0);
}

void renderScene(void) {

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glPushMatrix();

	positionCamera();
	AnimateSystems();
	DrawSystems();
	
	glPopMatrix();
	glutSwapBuffers();
}

int main(int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitWindowPosition(-1, -1);
	glutInitWindowSize(600, 600);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);

	glutCreateWindow("Falling cube demo");

	glutDisplayFunc(renderScene);
	glutIdleFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutKeyboardFunc(ProcessKeyboard);

#ifdef FULL_SCREEN
	glutFullScreen();
	glutSetCursor(GLUT_CURSOR_NONE);
#endif

	// Load the textures
	loadTextures();

	// Initialize the Win32 performance counter
	Timer_Init();

	// Set up the lights in the 3D world
	Init3D();

	// Initialize the physics systems
	InitSystems();

	glutMainLoop();

	return 0;
}